package com.gao.asmdemo.chess

import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.util.Log
import android.view.Gravity
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.widget.AppCompatTextView
import kotlin.math.abs
import kotlin.math.roundToInt

/**
 * 文件名：ChessBoardView <br/>
 * 描述：自定义棋盘
 *
 * 1,四个动作（上下左右）
 * 2, 4*4 的棋盘
 * 3，棋子合并
 *
 * @author king
 * @since 2022/07/21 14:32
 */
class ChessBoardView : ViewGroup {

    constructor(context: Context) : super(context)
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
    constructor(context: Context, attributeSet: AttributeSet, def: Int) : super(
        context,
        attributeSet,
        def
    )

    //给创建空的棋盘和棋子
    private val pointArray = Array(4) { x ->
        Array(4) { y ->
            ChessView(context)
        }
    }

    private var gameEnd = false

    init {
        addNewPoint()
        addNewPoint()
        addAllChild()
        //setWillNotDraw(false)
    }

    fun reset() {
        //重置
        pointArray.forEach { array ->
            array.forEach {
                it.num = 0
                it.text = ""
            }
        }
        addNewPoint()
        addNewPoint()
        gameEnd = false
    }

    private fun addAllChild() {
        pointArray.forEach { array ->
            array.forEach {
                addView(it)
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val s = MeasureSpec.makeMeasureSpec(measuredWidth / 4 - dpToPx(4), MeasureSpec.EXACTLY)
        for (x in 0..3) {
            for (y in 0..3) {
                pointArray[x][y].measure(s, s)
            }
        }
        super.onMeasure(widthMeasureSpec, widthMeasureSpec)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val width = measuredWidth
        val childWidth = width / 4
        for (x in 0..3) {
            for (y in 0..3) {
                pointArray[x][y].apply {
                    layout(
                        x * childWidth + dpToPx(2),
                        y * childWidth + dpToPx(2),
                        x * childWidth + childWidth - dpToPx(2),
                        y * childWidth + childWidth - dpToPx(2)
                    )
                }
            }
        }
    }

    /*override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //添加辅助线
        val paint = Paint()
        paint.strokeWidth = dpToPx(4).toFloat()
        paint.color = Color.CYAN
        canvas?.drawLine(0F, 0F, width.toFloat(), 0F, paint)
        canvas?.drawLine(0F, width.toFloat(), width.toFloat(), width.toFloat(), paint)
        canvas?.drawLine(0F, 0F, 0F, width.toFloat(), paint)
        canvas?.drawLine(width.toFloat(), 0F, width.toFloat(), width.toFloat(), paint)
        paint.strokeWidth = dpToPx(2).toFloat()
        canvas?.drawLine(0F, width / 4F, width.toFloat(), width / 4F, paint)
        canvas?.drawLine(0F, width / 4F * 2, width.toFloat(), width / 4F * 2, paint)
        canvas?.drawLine(0F, width / 4F * 3, width.toFloat(), width / 4F * 3, paint)



        canvas?.drawLine(width / 4F, 0F, width / 4F, width.toFloat(), paint)
        canvas?.drawLine(width / 4F * 2, 0F, width / 4F * 2, width.toFloat(), paint)
        canvas?.drawLine(width / 4F * 3, 0F, width / 4F * 3, width.toFloat(), paint)
    }*/

    fun move(orientation: Orientation) {
        if (gameEnd) {
            Toast.makeText(context, "游戏结束", Toast.LENGTH_SHORT).show()
            return
        }
        when (orientation) {
            Orientation.UP -> {
                //向上滑动
                //从上往下遍历,x轴不变，y轴递增
                for (x in 0..3) {
                    for (y in 1..3) {
                        val view = pointArray[x][y]
                        if (!view.isEmptyView()) {
                            mergeOrMoveChess(Point(x, y), orientation)
                        }
                    }
                }
            }
            Orientation.DOWN -> {
                //向下滑动
                //从上往下遍历,x轴不变，y轴递减
                for (x in 0..3) {
                    for (y in 2 downTo 0) {
                        val view = pointArray[x][y]
                        if (!view.isEmptyView()) {
                            mergeOrMoveChess(Point(x, y), orientation)
                        }
                    }
                }
            }
            Orientation.LEFT -> {
                //从左往右
                //从右往左遍历,x轴递减，y轴不变
                for (y in 0..3) {
                    for (x in 2 downTo 0) {
                        val view = pointArray[x][y]
                        if (!view.isEmptyView()) {
                            mergeOrMoveChess(Point(x, y), orientation)
                        }
                    }
                }
            }
            Orientation.RIGHT -> {
                //从右往左
                //从左往右遍历,x轴递增，y轴不变
                for (y in 0..3) {
                    for (x in 1..3) {
                        val view = pointArray[x][y]
                        if (!view.isEmptyView()) {
                            mergeOrMoveChess(Point(x, y), orientation)
                        }
                    }
                }
            }
        }
        if (checkEnd()) {
            Toast.makeText(context, "游戏结束", Toast.LENGTH_SHORT).show()
            return
        }
        addNewPoint()
    }

    //判断游戏是否结束
    private fun checkEnd(): Boolean {
        //1,有空白的就不结束
        //2,判断每个view的数字与隔壁的是否相同，如果都不同，则结束。如果有相同，则不结束
        for (x in 0..3) {
            for (y in 0..3) {
                pointArray[x][y].apply {
                    if (isEmptyView()) {
                        gameEnd = false
                        return false
                    } else {
                        val num = this.num
                        val topNum = try {
                            pointArray[x - 1][y].num
                        } catch (e: Exception) {
                            -1
                        }
                        val bottomNum = try {
                            pointArray[x + 1][y].num
                        } catch (e: Exception) {
                            -1
                        }
                        val leftNum = try {
                            pointArray[x][y - 1].num
                        } catch (e: Exception) {
                            -1
                        }
                        val rightNum = try {
                            pointArray[x][y + 1].num
                        } catch (e: Exception) {
                            -1
                        }
                        if (leftNum != -1 && leftNum == num) {
                            gameEnd = false
                            return false
                        }
                        if (rightNum != -1 && rightNum == num) {
                            gameEnd = false
                            return false
                        }
                        if (topNum != -1 && topNum == num) {
                            gameEnd = false
                            return false
                        }
                        if (bottomNum != -1 && bottomNum == num) {
                            gameEnd = false
                            return false
                        }
                    }
                }
            }
        }
        gameEnd = true
        return true
    }

    private fun transNum2Color(num: Int): Int {
        if (num == 0) return Color.WHITE
        var step = 0
        var catchNum = num
        var flag = true
        while (flag) {
            catchNum = catchNum shr 1
            step += 1
            flag = (catchNum != 0)
        }
        if (step > 10) step = 10
        return getCurrentColor(step / 10F, Color.WHITE, Color.RED)
    }

    //针对某个棋子，一次滑动的操作
    private fun mergeOrMoveChess(point: Point, orientation: Orientation) {
        val list = findCanMoveChessList(point, orientation)
        if (list.isEmpty()) {
            //合并不了
            return
        }
        //找到最后面的位置
        val lastPoint = list.last()
        //可以合并
        if (canMerge(point, lastPoint)) {
            //去合并
            mergePoint(point, lastPoint)
        } else {
            //不能合并，找到倒数第1个,如果是空的，则移动到倒数第1.否则移动到倒数第二
            if (pointArray[lastPoint.x][lastPoint.y].isEmptyView()) {
                move2NewPoint(point, lastPoint)
                return
            }
            if (list.size >= 2) {
                val movePoint = list[list.size - 2]
                move2NewPoint(point, movePoint)
            }
        }
    }

    private fun move2NewPoint(point: Point, movePoint: Point) {
        val oldView = pointArray[point.x][point.y]
        val newView = pointArray[movePoint.x][movePoint.y]
        newView.num = oldView.num
        oldView.num = 0
    }

    private fun mergePoint(point: Point, lastPoint: Point) {
        val oldView = pointArray[point.x][point.y]
        val newView = pointArray[lastPoint.x][lastPoint.y]
        newView.num += oldView.num
        oldView.num = 0
    }

    //判断两个棋子是否能合并
    private fun canMerge(point: Point, lastPoint: Point): Boolean {
        val nam1 = pointArray[point.x][point.y].num
        val nam2 = pointArray[lastPoint.x][lastPoint.y].num
        return nam1 == nam2
    }

    //在空白地方生成一个棋子
    private fun addNewPoint() {
        val emptyList = ArrayList<ChessView>(16)
        pointArray.forEach { list ->
            list.forEach {
                if (it.isEmptyView()) {
                    emptyList.add(it)
                }
            }
        }
        if (emptyList.size == 0) return
        //找到第一个
        val view = emptyList.removeAt((0 until emptyList.size).random())
        view.num = if ((0..99).random() % 2 == 0) {
            2
        } else {
            4
        }
        view.text = view.num.toString()
    }

    //根据手势找到可能合并的列表
    private fun findCanMoveChessList(point: Point, orientation: Orientation): List<Point> {
        val list = ArrayList<Point>(3)
        when (orientation) {
            Orientation.UP -> {
                //向上
                var pointY = point.y - 1
                while (true) {
                    val view = pointArray[point.x][pointY]
                    list.add(Point(point.x, pointY))
                    if (pointY == 0) {
                        return list
                    }
                    if (view.isEmptyView()) {
                        pointY -= 1
                    } else {
                        return list
                    }
                }
            }
            Orientation.DOWN -> {
                //向下
                var pointY = point.y + 1
                while (true) {
                    val view = pointArray[point.x][pointY]
                    list.add(Point(point.x, pointY))
                    if (pointY == 3) {
                        return list
                    }
                    if (view.isEmptyView()) {
                        pointY += 1
                    } else {
                        return list
                    }
                }
            }
            Orientation.LEFT -> {
                //从左往右
                var pointX = point.x + 1
                while (true) {
                    val view = pointArray[pointX][point.y]
                    list.add(Point(pointX, point.y))
                    if (pointX == 3) {
                        return list
                    }
                    if (view.isEmptyView()) {
                        pointX += 1
                    } else {
                        return list
                    }
                }
            }
            Orientation.RIGHT -> {
                //从右往左
                var pointX = point.x - 1
                while (true) {
                    val view = pointArray[pointX][point.y]
                    list.add(Point(pointX, point.y))
                    if (pointX == 0) {
                        return list
                    }
                    if (view.isEmptyView()) {
                        pointX -= 1
                    } else {
                        return list
                    }
                }
            }
        }
    }

    inner class ChessView(context: Context) :
        AppCompatTextView(context, null, 0) {

        var num: Int = 0
            set(value) {
                field = value
                setBackgroundColor(transNum2Color(field))
                text = if (field == 0) "" else field.toString()
            }

        init {
            setBackgroundColor(Color.WHITE)
            setTextColor(Color.BLACK)
            gravity = Gravity.CENTER
            textSize = 20F
        }

        fun isEmptyView(): Boolean = num == 0
    }

    private var dx = 0F
    private var dy = 0F

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                dx = event.x
                dy = event.y
            }
            MotionEvent.ACTION_UP -> {
                dx = event.x - dx
                dy = event.y - dy
                checkTouch()
            }
        }
        return true
    }

    private fun checkTouch() {
        if (abs(dx) > abs(dy)) {
            //左右移动
            if (abs(dx) >= width / 4) {
                //有效距离
                if (dx > 0) {
                    move(Orientation.LEFT)
                } else {
                    move(Orientation.RIGHT)
                }
            }
        } else {
            //上下移动
            if (abs(dy) >= width / 4) {
                //有效距离
                if (dy > 0) {
                    move(Orientation.DOWN)
                } else {
                    move(Orientation.UP)
                }
            }
        }
    }

    private fun dpToPx(dps: Int): Int {
        return (resources.displayMetrics.density * dps).roundToInt()
    }

    private fun getCurrentColor(fraction: Float, startColor: Int, endColor: Int): Int {
        val redCurrent: Int
        val blueCurrent: Int
        val greenCurrent: Int
        val alphaCurrent: Int
        val redStart = Color.red(startColor)
        val blueStart = Color.blue(startColor)
        val greenStart = Color.green(startColor)
        val alphaStart = Color.alpha(startColor)
        val redEnd = Color.red(endColor)
        val blueEnd = Color.blue(endColor)
        val greenEnd = Color.green(endColor)
        val alphaEnd = Color.alpha(endColor)
        val redDifference = redEnd - redStart
        val blueDifference = blueEnd - blueStart
        val greenDifference = greenEnd - greenStart
        val alphaDifference = alphaEnd - alphaStart
        redCurrent = (redStart + fraction * redDifference).toInt()
        blueCurrent = (blueStart + fraction * blueDifference).toInt()
        greenCurrent = (greenStart + fraction * greenDifference).toInt()
        alphaCurrent = (alphaStart + fraction * alphaDifference).toInt()
        return Color.argb(alphaCurrent, redCurrent, greenCurrent, blueCurrent)
    }
}